home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Software Explosion
/
Software Explosion (Fore-Matt Home Computing)(1996).iso
/
games
/
windows
/
life
/
life.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-05-28
|
35KB
|
963 lines
/****************************************************************************
*
* FILE: LIFE.C
*
* DESCRIPTION: Windows 3.0 Version of the classic simulation of "LIFE".
* This program incorporates a minor twist in the basic
* algorithm by utilizing color to represent various
* life "phases". The final phase of any life cycle is
* always death!
*
* This software is hereby placed in the public domain.
* Use and abuse it at your own risk!
*
*
* AUTHOR: Tom Wheeler
* 31294 Morlock
* Livonia, Mich. 48152
* [72037,1742]
*
*****************************************************************************
*
* DATE VER DESCRIPTION
* ---------- --- ---------------------------------------------------
* 05/20/91 1.0 Initial Development 1.0
* 05/22/91 1.1 Added Color Support
*
****************************************************************************/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "life.h"
#define CLASSNAME "LifeClass"
#define APPNAME "Life"
#define DEAD 0 /* state: cell is dead */
#define ALIVE 0x1 /* state: cell is alive */
#define CHILD 0x2 /* state: cell is a child */
#define ADOLESCENT 0x4 /* state: cell is an adolescent */
#define ADULT 0x8 /* state: cell is an adult */
#define MIDAGED 0x10 /* state: cell is middle aged */
#define RETIRED 0x20 /* state: cell is retirement age */
#define SENIOR 0x40 /* state: cell is a senior citizen */
#define METHUSULA 0x80 /* state: cell is to be terminated */
#define AGECYCLE 10 /* # of cycles till next age */
#define MAX_XCELL 100 /* max number of horizontal cells */
#define MAX_YCELL 100 /* max number of vertical cells */
#define DEFAULT_WIDTH 15 /* default number of cells to display */
#define DEFAULT_HEIGHT 15 /* default number of cells to display */
#define CYCLE_TIMER 1
/* Macro to get a random integer within a specified range */
#define getrandom( min, max ) ((rand() % (int)(((max)+1) - (min))) + (min))
typedef unsigned char UCHAR;
/* MATRIX defines the cell matrix used to represent the grid of entities.
* Life states are represented as the low byte of the matrix entry, the high
* byte is used to is used to keep a count representing the age of the
* individual entities.
*/
typedef int MATRIX[MAX_XCELL+2][MAX_YCELL+2];
typedef struct _Life {
int CellX; /* individual cell width */
int CellY; /* individual cell height */
int TopCellX; /* horiz. id of first cell displayed */
int TopCellY; /* vert. id of frist cell displayed */
int CellsPerPageX; /* num of visible X cells on screen */
int CellsPerPageY; /* num of visible Y cells on screen */
BOOL Grid; /* grid on/off state */
BOOL Run; /* run state of matrix */
MATRIX Matrix; /* state matrix */
}LIFE;
typedef LIFE *PLIFE;
MATRIX Scratch; /* temporary cell matrix */
HWND hInst = NULL; /* instance variable */
HMENU hMenuFrame = NULL; /* instance menu handle */
HWND hWndFrame = NULL; /* instance window handle */
HANDLE hAccelFrame = NULL; /* instance keyboard accelerator */
HANDLE hMatrix = NULL; /* handle to cell matrix structure */
HPEN hBluePen = NULL; /* Blue drawing pen */
HPEN hGreenPen = NULL; /* Green drawing pen */
HPEN hCyanPen = NULL; /* Cyan drawing pen */
HPEN hRedPen = NULL; /* Red drawing pen */
HPEN hMagentaPen = NULL; /* Magenta drawing pen */
HPEN hBrownPen = NULL; /* Brown drawing pen */
HPEN hGrayPen = NULL; /* Gray drawing pen */
HPEN hLightRedPen = NULL; /* Light Red drawing pen */
HPEN hWhitePen = NULL; /* White drawing pen */
HBRUSH hBlueBrush = NULL; /* Solid Blue brush */
HBRUSH hGreenBrush = NULL; /* Solid Green brush */
HBRUSH hCyanBrush = NULL; /* Solid Cyan brush */
HBRUSH hRedBrush = NULL; /* Solid Red brush */
HBRUSH hMagentaBrush = NULL; /* Solid Magenta brush */
HBRUSH hBrownBrush = NULL; /* Solid Brown brush */
HBRUSH hGrayBrush = NULL; /* Solid Gray brush */
HBRUSH hLightRedBrush = NULL; /* Solid Light Red brush */
HBRUSH hWhiteBrush = NULL; /* Solid White brush */
PLIFE pLife = NULL; /* pointer to cell life structure */
int iSmall; /* size in pixels of a small cell */
int iNorm; /* size in pixels of a normal cell */
int iLarge; /* size in pixels of a large cell */
int iCycleTime = 250; /* default timer (milliseconds) */
WORD wCycleTimer = 0; /* timer used for life cycling */
/****************************************************************************
* void DrawGridLines(HDC hDC,RECT *rect)
*
* Description: DrawsGridLines on the Life Display Window
*
* Input: hDC - Device Context to draw on
* rect - pointer to client rectangle
*
* Output: N/A
****************************************************************************/
void DrawGridLines(HDC hDC,RECT *rect)
{
int iX,iY;
SelectObject(hDC,hGrayPen);
for(iX = pLife->CellX; iX < rect->right; iX += pLife->CellX) {
MoveTo(hDC,iX,0);
LineTo(hDC,iX,rect->bottom);
}
for(iY = pLife->CellY; iY < rect->bottom; iY += pLife->CellY) {
MoveTo(hDC,0,iY);
LineTo(hDC,rect->right,iY);
}
}
/****************************************************************************
* void DrawCell(HDC hMouseMoveDC,int iXPos,int iYPos,UCHAR ucState)
*
* Description: Draws the cell at the indicated grid position in the color
* appropriate to its current state.
*
* Input: hDC - Device Context to draw on
* iXPos
* iYPos - Screen Matrix position to draw at
* ucState - State to draw
*
* Output: N/A
****************************************************************************/
void DrawCell(HDC hDC,int iXPos,int iYPos,UCHAR ucState)
{
int iX,iY;
HPEN hPen;
HBRUSH hBrush;
iX = iXPos * pLife->CellX;
iY = iYPos * pLife->CellY;
if(ucState & ALIVE) {
if(ucState & SENIOR) {
hPen = hBluePen;
hBrush = hBlueBrush;
}
else if(ucState & RETIRED) {
hPen = hBrownPen;
hBrush = hBrownBrush;
}
else if(ucState & MIDAGED) {
hPen = hMagentaPen;
hBrush = hMagentaBrush;
}
else if(ucState & ADULT) {
hPen = hRedPen;
hBrush = hRedBrush;
}
else if(ucState & ADOLESCENT) {
hPen = hCyanPen;
hBrush = hCyanBrush;
}
else if(ucState & CHILD) {
hPen = hGreenPen;
hBrush = hGreenBrush;
}
}
else {
hPen = hWhitePen;
hBrush = hWhiteBrush;
}
SelectObject(hDC,hPen);
SelectObject(hDC,hBrush);
Rectangle(hDC,iX+2,iY+2,iX+pLife->CellX-1,iY+pLife->CellY-1);
}
/****************************************************************************
* void DrawMatrix(HDC hDC)
*
* Description: Draws The Matrix (only draws the cells that will be visible
* in the display window)
*
* Input: hDC - Device Context to draw on
*
* Output: N/A
****************************************************************************/
void DrawMatrix(HDC hDC)
{
int iX,iY,iXPos,iYPos,iCellTemp;
for(iXPos = 0,iX = pLife->TopCellX;
iX < pLife->CellsPerPageX + pLife->TopCellX; iX++,iXPos++) {
for(iYPos = 0,iY = pLife->TopCellY;
iY < pLife->CellsPerPageY + pLife->TopCellY; iY++,iYPos++) {
iCellTemp = pLife->Matrix[iX][iY] & 0xff;
if(iCellTemp & ALIVE)
DrawCell(hDC,iXPos,iYPos,(UCHAR)iCellTemp);
}
}
}
/****************************************************************************
* void CycleMatrix(HDC hDC)
*
* Description: Runs the Life Algorithm on the Cell Matrix (only updates
* the cells that would be visible, the matrix is forced to
* wrap at the limits of the screen matrix).
*
* Input: hDC - Device Context to draw on
*
* Output: N/A
****************************************************************************/
void CycleMatrix(HDC hDC)
{
register int iX,iY;
int iXPos,iYPos,iNeighbor,iCellTemp;
UCHAR ucState;
BOOL bNoneAlive = TRUE;
for(iX = pLife->TopCellX,iXPos = 0; iX < (pLife->CellsPerPageX +
pLife->TopCellX); iX++,iXPos++) {
for(iY = pLife->TopCellY,iYPos = 0; iY < (pLife->CellsPerPageY +
pLife->TopCellY); iY++,iYPos++) {
iNeighbor = 0;
if(pLife->Matrix[iX-1][iY] & ALIVE)
iNeighbor++;
if(pLife->Matrix[iX+1][iY] & ALIVE)
iNeighbor++;
if(pLife->Matrix[iX][iY-1] & ALIVE)
iNeighbor++;
if(pLife->Matrix[iX][iY+1] & ALIVE)
iNeighbor++;
if(pLife->Matrix[iX-1][iY-1] & ALIVE)
iNeighbor++;
if(pLife->Matrix[iX+1][iY+1] & ALIVE)
iNeighbor++;
if(pLife->Matrix[iX-1][iY+1] & ALIVE)
iNeighbor++;
if(pLife->Matrix[iX+1][iY-1] & ALIVE)
iNeighbor++;
ucState = (UCHAR)pLife->Matrix[iX][iY];
if(ucState == DEAD) { /* birth of a cell if 3 neighbors present */
if(iNeighbor == 3) {
Scratch[iX][iY] = ALIVE | CHILD;
DrawCell(hDC,iXPos,iYPos,ALIVE | CHILD);
}
else {
Scratch[iX][iY] = pLife->Matrix[iX][iY];
}
}
else {
bNoneAlive = FALSE;
if((iNeighbor < 2) || (iNeighbor > 3)) { /* kill it off */
Scratch[iX][iY] = DEAD;
DrawCell(hDC,iXPos,iYPos,DEAD);
}
else {
/* Keep cell and increment cycle count. If cycle count limit
* is reached, advance cell to next life cycle
*/
Scratch[iX][iY] = pLife->Matrix[iX][iY] + 0x100;
if(!((Scratch[iX][iY] & 0xff00) % (AGECYCLE << 8))) {
iCellTemp = ((Scratch[iX][iY] & 0xff) << 1) | ALIVE;
if(iCellTemp <= METHUSULA)
Scratch[iX][iY] = (Scratch[iX][iY] & 0xff00) + iCellTemp;
else {
/* if it has aged past SENIOR, kill it off */
Scratch[iX][iY] = iCellTemp = DEAD;
}
DrawCell(hDC,iXPos,iYPos,(UCHAR)iCellTemp);
}
}
}
}
}
/* copy all changes to permanent cell matrix */
memmove(&pLife->Matrix,&Scratch,sizeof(MATRIX));
/* randomize the matrix if no alive cells are displayed */
if(bNoneAlive) {
for(iX = pLife->TopCellX; iX < (pLife->CellsPerPageX +
pLife->TopCellX); iX++) {
for(iY = pLife->TopCellY; iY < (pLife->CellsPerPageY +
pLife->TopCellY); iY++) {
if(getrandom(0,1)) {
pLife->Matrix[iX][iY] = ALIVE | CHILD;
}
}
}
}
}
/****************************************************************************
* void UpdateScrollBarRange(HWND hWnd)
*
* Description: Updates the Scroll Bar Ranges
*
* Input: hWnd - Window Handle owning the scroll bars
*
* Output: N/A
****************************************************************************/
void UpdateScrollBarRange(HWND hWnd)
{
RECT rect;
GetClientRect(hWnd,&rect);
pLife->CellsPerPageX = (rect.right / pLife->CellX) + 1;
pLife->CellsPerPageY = (rect.bottom / pLife->CellY) + 1;
pLife->TopCellX = max(1,min(pLife->TopCellX,MAX_XCELL -
pLife->CellsPerPageX));
pLife->TopCellY = max(1,min(pLife->TopCellY,MAX_YCELL -
pLife->CellsPerPageY));
SetScrollRange(hWnd,SB_HORZ,1,MAX_XCELL - pLife->CellsPerPageX,TRUE);
SetScrollRange(hWnd,SB_VERT,1,MAX_YCELL - pLife->CellsPerPageY,TRUE);
}
/****************************************************************************
* BOOL InitLife(void)
*
* Description: Life Initialization
*
* Input: N/A
*
* Output: Fails if no memory available
****************************************************************************/
BOOL InitLife(void)
{
int iWidth,iHeight,iX,iY;
/* allocate local storage to hold the Life Cell Matrix */
if((hMatrix = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT,
sizeof(LIFE))) == NULL)
return FALSE;
pLife = (PLIFE)LocalLock(hMatrix); /* lock down the memory permanently */
/* set the default Life cell values */
iX = GetSystemMetrics(SM_CXSCREEN);
iY = GetSystemMetrics(SM_CYSCREEN);
iNorm = iY / 25;
iSmall = iNorm / 2;
iLarge = iNorm * 2;
pLife->CellX = iNorm;
pLife->CellY = iNorm;
pLife->Grid = TRUE;
pLife->Run = FALSE;
pLife->TopCellX = pLife->TopCellY = 1;
CheckMenuItem(hMenuFrame,MENU_NORM,MF_BYCOMMAND | MF_CHECKED);
/* initialize all cells to DEAD */
for(iWidth = 0; iWidth < MAX_XCELL; iWidth++)
for(iHeight = 0; iHeight < MAX_YCELL; iHeight++)
pLife->Matrix[iWidth][iHeight] = DEAD;
iWidth = (pLife->CellX * DEFAULT_WIDTH) +
(GetSystemMetrics(SM_CXFRAME) * 2) +
GetSystemMetrics(SM_CXHTHUMB);
iHeight = (pLife->CellY * DEFAULT_HEIGHT) +
(GetSystemMetrics(SM_CYFRAME) * 2) +
GetSystemMetrics(SM_CYMENU) +
GetSystemMetrics(SM_CYVTHUMB) +
GetSystemMetrics(SM_CYCAPTION);
SetWindowPos(hWndFrame,NULL,(iX - iWidth) / 2,(iY - iHeight) / 2,
iWidth,iHeight,SWP_NOZORDER);
}
/****************************************************************************
* CenterDialog(HWND hDlg)
*
* Description: Function to Center a Dialog Box on the Screen
*
* Input: hDlg - Handle to Dialog Box to be centered
*
* Output: N/A
*****************************************************************************/
void CenterDialog(HWND hDlg)
{
RECT rect;
int iX,iY,iNewX,iNewY;
GetWindowRect(hDlg,(RECT far *)&rect);
iX = GetSystemMetrics(SM_CXSCREEN);
iY = GetSystemMetrics(SM_CYSCREEN);
iNewX = (iX/2) - ((rect.right-rect.left)/2);
iNewY = (iY/2) - ((rect.bottom-rect.top)/2);
SetWindowPos(hDlg,NULL,max(iNewX,0),max(iNewY,0),rect.right-rect.left,
rect.bottom-rect.top,SWP_NOZORDER);
}
/****************************************************************************
*
* BOOL FAR PASCAL TimerEditDlgProc(HWND hDlg,int iMessage,WORD wParam,
* LONG lParam)
*
* DESCRIPTION: Life Cycle Time Edit Dialog Box Proc
*
* INPUT: hDlg - Handle to Dialog Box
* iMessage - Dialog Box message
* wParam
* lParam - Standard Windows message parameters
* Called externally from WINDOWS - Exported Function
*
* OUTPUT: N/A
*
****************************************************************************/
BOOL FAR PASCAL TimerEditDlgProc(HWND hDlg,int iMessage,WORD wParam,
LONG lParam)
{
BOOL bTransFlag;
WORD wVal;
char szTime[10];
switch (iMessage) {
case WM_COMMAND:
switch(wParam) {
case IDOK:
wVal = GetDlgItemInt(hDlg,TIMER_EDIT,(BOOL FAR *)&bTransFlag,
FALSE);
if((!bTransFlag) || (wVal < 100) || (wVal > 999)) {
MessageBeep(0);
sprintf(szTime,"%d",iCycleTime);
SetDlgItemText(hDlg,TIMER_EDIT,(LPSTR)szTime);
}
else {
iCycleTime = wVal;
EndDialog(hDlg,TRUE);
}
break;
case IDCANCEL:
EndDialog(hDlg,FALSE);
break;
}
break;
case WM_INITDIALOG:
sprintf(szTime,"%d",iCycleTime);
SetDlgItemText(hDlg,TIMER_EDIT,(LPSTR)szTime);
CenterDialog(hDlg);
return(TRUE);
default:
return(FALSE);
}
return(FALSE);
}
/****************************************************************************
* AboutDlgProc(hDlg,iMessage,wParam,lParam)
*
* Description: About Dialog Box Procedure
*
* Input: hDlg - Handle to Dialog Box
* uMessage - Dialog Box message
* wParam
* lParam - Standard Windows message parameters
* Called externally from WINDOWS
*
* Output: N/A
*****************************************************************************/
BOOL FAR PASCAL AboutDlgProc(HWND hDlg,unsigned iMessage,
WORD wParam,LONG lParam)
{
BOOL Ret;
char szTimeStamp[128];
Ret = FALSE;
switch (iMessage)
{
case WM_INITDIALOG:
sprintf(szTimeStamp,"Built: %s %s",__DATE__,__TIME__);
SetDlgItemText(hDlg,ABOUT_TIMESTAMP,szTimeStamp);
CenterDialog(hDlg);
Ret = TRUE;
break;
case WM_COMMAND:
EndDialog(hDlg, TRUE);
Ret = TRUE;
break;
}
return Ret;
}
/****************************************************************************
* WndProc(hWnd,iMessage,wParam,lParam)
*
* Description: Main Window Proc
*
* Input: hWnd - Handle to window
* iMessage - Window message number
* wParam,
* lParam - Standard Windows message parameters
* Called externally from WINDOWS
*
* Output: Calls DefWindowProc() while active,returns 0L ((LONG) FALSE)
* when terminating.
****************************************************************************/
long FAR PASCAL WndProc(HWND hWnd,unsigned iMessage,WORD wParam,LONG lParam)
{
FARPROC lpProc;
long Ret;
PAINTSTRUCT ps;
static RECT rect;
HDC hDC;
WORD wXMouse,wYMouse;
UCHAR ucState;
static int iXDown,iYDown;
static BOOL bMouseDown = FALSE,bIconic = FALSE;
static HDC hMouseMoveDC = NULL;
int iXCell,iYCell;
Ret = (long)FALSE;
switch(iMessage)
{
case WM_COMMAND:
switch (wParam)
{
case MENU_RUN:
if(pLife->Run) {
/* stop the Cycle Timer */
if(wCycleTimer) {
KillTimer(hWnd,CYCLE_TIMER);
wCycleTimer = 0;
}
ModifyMenu(hMenuFrame,MENU_RUN,MF_BYCOMMAND,MENU_RUN,
"St&art\t^R");
EnableMenuItem(hMenuFrame,MENU_STEP,MF_ENABLED);
pLife->Run = FALSE;
}
else {
/*start the cycle timer */
if((wCycleTimer =
SetTimer(hWnd,CYCLE_TIMER,iCycleTime,NULL)) == 0) {
MessageBox(hWnd,"No More System Timers!",
NULL,MB_OK | MB_ICONEXCLAMATION);
break;
}
ModifyMenu(hMenuFrame,MENU_RUN,MF_BYCOMMAND,MENU_RUN,
"St&op\t^R");
pLife->Run = TRUE;
EnableMenuItem(hMenuFrame,MENU_STEP,MF_GRAYED);
}
break;
case MENU_STEP:
hDC = GetDC(hWnd);
CycleMatrix(hDC);
ReleaseDC(hWnd,hDC);
InvalidateRect(hWnd,NULL,FALSE);
break;
case MENU_CLEAR:
/* if running, stop before clearing */
if(pLife->Run)
SendMessage(hWnd,WM_COMMAND,MENU_RUN,0L);
for(iXCell = 0; iXCell < MAX_XCELL; iXCell++) {
for(iYCell = 0; iYCell < MAX_YCELL; iYCell++) {
pLife->Matrix[iXCell][iYCell] = DEAD;
Scratch[iXCell][iYCell] = DEAD;
}
}
InvalidateRect(hWnd,NULL,TRUE);
break;
case MENU_GRID:
if(pLife->Grid) {
ModifyMenu(hMenuFrame,MENU_GRID,MF_BYCOMMAND,MENU_GRID,
"&Gridlines On\t^G");
pLife->Grid = FALSE;
}
else {
ModifyMenu(hMenuFrame,MENU_GRID,MF_BYCOMMAND,MENU_GRID,
"&Gridlines Off\t^G");
pLife->Grid = TRUE;
}
InvalidateRect(hWnd,NULL,TRUE);
break;
case MENU_TIMER:
lpProc = MakeProcInstance((FARPROC) TimerEditDlgProc,hInst);
DialogBox(hInst,"TIMERDLGBOX",hWnd,lpProc);
FreeProcInstance(lpProc);
/* if already running, update the timer */
if(wCycleTimer) {
KillTimer(hWnd,CYCLE_TIMER);
if((wCycleTimer =
SetTimer(hWnd,CYCLE_TIMER,iCycleTime,NULL)) == 0) {
MessageBox(hWnd,"No More System Timers!",
NULL,MB_OK | MB_ICONEXCLAMATION);
break;
}
}
break;
case MENU_SMALL:
CheckMenuItem(hMenuFrame,MENU_SMALL,MF_BYCOMMAND | MF_CHECKED);
CheckMenuItem(hMenuFrame,MENU_NORM,MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem(hMenuFrame,MENU_LARGE,MF_BYCOMMAND | MF_UNCHECKED);
pLife->CellX = iSmall;
pLife->CellY = iSmall;
UpdateScrollBarRange(hWnd);
InvalidateRect(hWnd,NULL,TRUE);
break;
case MENU_NORM:
CheckMenuItem(hMenuFrame,MENU_SMALL,MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem(hMenuFrame,MENU_NORM,MF_BYCOMMAND | MF_CHECKED);
CheckMenuItem(hMenuFrame,MENU_LARGE,MF_BYCOMMAND | MF_UNCHECKED);
pLife->CellX = iNorm;
pLife->CellY = iNorm;
UpdateScrollBarRange(hWnd);
InvalidateRect(hWnd,NULL,TRUE);
break;
case MENU_LARGE:
CheckMenuItem(hMenuFrame,MENU_SMALL,MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem(hMenuFrame,MENU_NORM,MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem(hMenuFrame,MENU_LARGE,MF_BYCOMMAND | MF_CHECKED);
pLife->CellX = iLarge;
pLife->CellY = iLarge;
UpdateScrollBarRange(hWnd);
InvalidateRect(hWnd,NULL,TRUE);
break;
case MENU_ABOUT:
lpProc = MakeProcInstance((FARPROC) AboutDlgProc,hInst);
DialogBox(hInst,"ABOUTBOX",hWnd,lpProc);
FreeProcInstance(lpProc);
break;
}
break;
case WM_TIMER:
hDC = GetDC(hWnd);
CycleMatrix(hDC);
ReleaseDC(hWnd,hDC);
if(!bIconic)
InvalidateRect(hWnd,NULL,FALSE);
break;
case WM_LBUTTONDOWN:
iXDown = -1;
iYDown = -1;
bMouseDown = TRUE;
hMouseMoveDC = GetDC(hWnd);
GetClientRect(hWnd,&rect);
SelectObject(hMouseMoveDC,GetStockObject(WHITE_PEN));
break;
case WM_LBUTTONUP:
/* force draw at current position */
SendMessage(hWnd,WM_MOUSEMOVE,wParam,lParam);
if(bMouseDown) {
bMouseDown = FALSE;
SelectObject(hMouseMoveDC,GetStockObject(BLACK_PEN));
ReleaseDC(hWnd,hMouseMoveDC);
}
break;
case WM_MOUSEMOVE:
if(bMouseDown) {
wXMouse = LOWORD(lParam);
wYMouse = HIWORD(lParam);
/* translate mouse position into cell coordinates */
iXCell = (int)wXMouse / pLife->CellX;
iYCell = (int)wYMouse / pLife->CellY;
if((iXCell != iXDown) || (iYCell != iYDown)) {
iXDown = iXCell;
iYDown = iYCell;
ucState = (UCHAR)pLife->Matrix[iXCell+pLife->TopCellX]
[iYCell+pLife->TopCellY];
ucState = (UCHAR)((ucState & ALIVE) ? DEAD : ALIVE | CHILD);
pLife->Matrix[iXCell+pLife->TopCellX]
[iYCell+pLife->TopCellY] = ucState;
DrawCell(hMouseMoveDC,iXCell,iYCell,ucState);
}
}
break;
case WM_NCMOUSEMOVE:
/* look to see if the mouse is outside the client area, if so and
* the mouse is being captured then release it
*/
if(bMouseDown) {
bMouseDown = FALSE;
SelectObject(hMouseMoveDC,GetStockObject(BLACK_PEN));
ReleaseDC(hWnd,hMouseMoveDC);
}
break;
case WM_VSCROLL:
switch(wParam) {
case SB_LINEUP:
pLife->TopCellY -= 1;
break;
case SB_PAGEUP:
pLife->TopCellY -= pLife->CellsPerPageY;
break;
case SB_LINEDOWN:
pLife->TopCellY += 1;
break;
case SB_PAGEDOWN:
pLife->TopCellY += pLife->CellsPerPageY;
break;
case SB_THUMBPOSITION:
pLife->TopCellY = LOWORD(lParam);
break;
default:
break;
}
pLife->TopCellY = max(1,min(pLife->TopCellY,MAX_YCELL -
pLife->CellsPerPageY));
if(pLife->TopCellY != GetScrollPos(hWnd,SB_VERT)) {
iXDown = -1;
iYDown = -1;
SetScrollPos(hWnd,SB_VERT,pLife->TopCellY,TRUE);
InvalidateRect(hWnd,NULL,TRUE);
}
break;
case WM_HSCROLL:
switch(wParam) {
case SB_LINEUP:
pLife->TopCellX -= 1;
break;
case SB_PAGEUP:
pLife->TopCellX -= pLife->CellsPerPageX;
break;
case SB_LINEDOWN:
pLife->TopCellX += 1;
break;
case SB_PAGEDOWN:
pLife->TopCellX += pLife->CellsPerPageX;
break;
case SB_THUMBPOSITION:
pLife->TopCellX = LOWORD(lParam);
break;
default:
break;
}
pLife->TopCellX = max(1,min(pLife->TopCellX,MAX_XCELL -
pLife->CellsPerPageX));
if(pLife->TopCellX != GetScrollPos(hWnd,SB_HORZ)) {
iXDown = -1;
iYDown = -1;
SetScrollPos(hWnd,SB_HORZ,pLife->TopCellX,TRUE);
InvalidateRect(hWnd,NULL,TRUE);
}
break;
case WM_KEYDOWN:
switch(wParam) {
case VK_PRIOR:
SendMessage(hWnd,WM_VSCROLL,SB_PAGEUP,0L);
break;
case VK_NEXT:
SendMessage(hWnd,WM_VSCROLL,SB_PAGEDOWN,0L);
break;
case VK_UP:
SendMessage(hWnd,WM_VSCROLL,SB_LINEUP,0L);
break;
case VK_DOWN:
SendMessage(hWnd,WM_VSCROLL,SB_LINEDOWN,0L);
break;
case VK_LEFT:
SendMessage(hWnd,WM_HSCROLL,SB_PAGEUP,0L);
break;
case VK_RIGHT:
SendMessage(hWnd,WM_HSCROLL,SB_PAGEDOWN,0L);
break;
}
break;
case WM_SIZE:
UpdateScrollBarRange(hWnd);
break;
case WM_PAINT:
hDC = BeginPaint(hWnd,&ps);
if(pLife->Grid)
DrawGridLines(hDC,&ps.rcPaint);
DrawMatrix(hDC);
EndPaint(hWnd,&ps);
break;
case WM_SYSCOMMAND:
switch(wParam) {
case SC_MINIMIZE:
bIconic = TRUE;
break;
case SC_MAXIMIZE:
case SC_RESTORE:
bIconic = FALSE;
break;
}
return DefWindowProc(hWnd, iMessage, wParam, lParam);
break;
case WM_CREATE:
hBluePen = CreatePen(PS_SOLID,1,RGB(0,0,128));
hGreenPen = CreatePen(PS_SOLID,1,RGB(0,128,0));
hCyanPen = CreatePen(PS_SOLID,1,RGB(0,128,128));
hRedPen = CreatePen(PS_SOLID,1,RGB(128,0,0));
hMagentaPen = CreatePen(PS_SOLID,1,RGB(128,0,128));
hBrownPen = CreatePen(PS_SOLID,1,RGB(128,128,0));
hGrayPen = CreatePen(PS_SOLID,1,RGB(192,192,192));
hLightRedPen = CreatePen(PS_SOLID,1,RGB(255,0,0));
hWhitePen = CreatePen(PS_SOLID,1,RGB(255,255,255));
hBlueBrush = CreateSolidBrush(RGB(0,0,128));
hGreenBrush = CreateSolidBrush(RGB(0,128,0));
hCyanBrush = CreateSolidBrush(RGB(0,128,128));
hRedBrush = CreateSolidBrush(RGB(128,0,0));
hMagentaBrush = CreateSolidBrush(RGB(128,0,128));
hBrownBrush = CreateSolidBrush(RGB(128,128,0));
hGrayBrush = CreateSolidBrush(RGB(192,192,192));
hLightRedBrush = CreateSolidBrush(RGB(255,0,0));
hWhiteBrush = CreateSolidBrush(RGB(255,255,255));
srand((unsigned)time(NULL));
break;
case WM_CLOSE:
if(wCycleTimer) {
KillTimer(hWnd,CYCLE_TIMER);
wCycleTimer = 0;
}
if(hMatrix != NULL) {
LocalUnlock(hMatrix);
LocalFree(hMatrix);
}
DeleteObject(hBluePen);
DeleteObject(hGreenPen);
DeleteObject(hCyanPen);
DeleteObject(hRedPen);
DeleteObject(hMagentaPen);
DeleteObject(hBrownPen);
DeleteObject(hGrayPen);
DeleteObject(hWhitePen);
DeleteObject(hBlueBrush);
DeleteObject(hGreenBrush);
DeleteObject(hCyanBrush);
DeleteObject(hRedBrush);
DeleteObject(hMagentaBrush);
DeleteObject(hBrownBrush);
DeleteObject(hGrayBrush);
DeleteObject(hWhiteBrush);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, iMessage, wParam, lParam);
}
return 0L;
}
/****************************************************************************
* int PASCAL WinMain(HANDLE hInstance,HANDLE hPrevInstance,
* LPSTR lpszCmdLine,int nCmdShow)
*
* Description: Life Main Window Function
*
* Input: hInstance - Window instance handle
* hPrevInstance - Previous Instance Handle
* lpszCmdLine - Window Activation Parameters (none req.)
* nCmdShow - Show Window or Icon command
* Called externally from WINDOWS
*
* Output: Handles Windows message loop,terminates to Windows
*****************************************************************************/
int PASCAL WinMain(HANDLE hInstance,HANDLE hPrevInstance,LPSTR lpszCmdLine,
int nCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASS wc;
hInst = hInstance;
if(!hPrevInstance)
{
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance,"LIFEICON");
wc.hCursor = LoadCursor(hInstance,"HAND");
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "MainMenu";
wc.lpszClassName = CLASSNAME;
if(!RegisterClass(&wc)) {
MessageBox(NULL, "Initialization Error!", APPNAME,
MB_OK | MB_ICONHAND | MB_SYSTEMMODAL);
return TRUE;
}
}
hWnd = CreateWindow(CLASSNAME, /* Window class name */
APPNAME, /* window caption */
WS_OVERLAPPEDWINDOW | WS_VSCROLL |
WS_HSCROLL, /* window style */
CW_USEDEFAULT, /* initial x (horiz) position */
CW_USEDEFAULT, /* initial y (vert) position */
CW_USEDEFAULT, /* initial x size */
CW_USEDEFAULT, /* initial y size */
NULL, /* parent window handle */
NULL, /* window menu handle */
hInstance, /* program instance handle */
NULL); /* create parameters */
/* initialize variables */
hWndFrame = hWnd; /* global parent window */
hMenuFrame = GetMenu(hWnd); /* global menu handle */
hAccelFrame = LoadAccelerators(hInstance,"LifeKeys"); /* key accelerator */
/* initialize the Life cell structure */
if(!InitLife())
return TRUE;
ShowWindow(hWnd,nCmdShow); /* shows the window */
/* message loop */
while(GetMessage(&msg,NULL,0,0)) {
if(!TranslateAccelerator(hWnd,hAccelFrame,&msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}